﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;

namespace LockCrypt {
    /// <summary>
    /// Utility for converting HSB to RGB colours.
    /// Credit to from http://www.vbaccelerator.com/home/NET/Code/Libraries/Graphics/HLS_to_RGB/article.asp
    /// </summary>
    public class HLSRGB {
        private byte red = 0;
        private byte green = 0;
        private byte blue = 0;

        private float hue = 0;
        private float luminance = 0;
        private float saturation = 0;

        public byte Red {
            get {
                return red;
            }
            set {
                red = value;
                ToHLS();
            }
        }
        public byte Green {
            get {
                return green;
            }
            set {
                green = value;
                ToHLS();
            }
        }
        public byte Blue {
            get {
                return blue;
            }
            set {
                blue = value;
                ToHLS();
            }
        }
        public float Luminance {
            get { return luminance; }
            set {
                if((luminance < 0.0f) || (luminance > 1.0f)) {
                    throw new ArgumentOutOfRangeException("Luminance", "Luminance must be between 0.0 and 1.0");
                }
                luminance = value;
                ToRGB();
            }
        }
        public float Hue {
            get { return hue; }
            set {
                if((hue < 0.0f) || (hue > 360.0f)) {
                    throw new ArgumentOutOfRangeException("Hue", "Hue must be between 0.0 and 360.0");
                }
                hue = value;
                ToRGB();
            }
        }
        public float Saturation {
            get { return saturation; }
            set {
                if((saturation < 0.0f) || (saturation > 1.0f)) {
                    throw new ArgumentOutOfRangeException("Saturation", "Saturation must be between 0.0 and 1.0");
                }
                saturation = value;
                ToRGB();
            }
        }

        public Color Color {
            get {
                Color c = Color.FromRgb(red, green, blue);
                return c;
            }
            set {
                red = value.R;
                green = value.G;
                blue = value.B;
                ToHLS();
            }
        }

        public void LightenColor(float lightenBy) {
            luminance *= (1.0f + lightenBy);
            if(luminance > 1.0f) {
                luminance = 1.0f;
            }
            ToRGB();
        }

        public void DarkenColor(float darkenBy) {
            luminance *= darkenBy;
            ToRGB();
        }


        public HLSRGB(Color c) {
            red = c.R;
            green = c.G;
            blue = c.B;
            ToHLS();
        }

        public HLSRGB(float hue, float luminance, float saturation) {
            this.hue = hue;
            this.luminance = luminance;
            this.saturation = saturation;
            ToRGB();
        }

        public HLSRGB(byte red, byte green, byte blue) {
            this.red = red;
            this.green = green;
            this.blue = blue;
        }

        public HLSRGB(HLSRGB hlsrgb) {
            this.red = hlsrgb.Red;
            this.blue = hlsrgb.Blue;
            this.green = hlsrgb.Green;
            this.luminance = hlsrgb.Luminance;
            this.hue = hlsrgb.Hue;
            this.saturation = hlsrgb.Saturation;
        }

        public HLSRGB() {
        }

        private void ToHLS() {
            byte minval = Math.Min(red, Math.Min(green, blue));
            byte maxval = Math.Max(red, Math.Max(green, blue));

            float mdiff = (float)(maxval - minval);
            float msum = (float)(maxval + minval);

            luminance = msum / 510.0f;

            if(maxval == minval) {
                saturation = 0.0f;
                hue = 0.0f;
            } else {
                float rnorm = (maxval - red) / mdiff;
                float gnorm = (maxval - green) / mdiff;
                float bnorm = (maxval - blue) / mdiff;

                saturation = (luminance <= 0.5f) ? (mdiff / msum) : (mdiff / (510.0f - msum));

                if(red == maxval) {
                    hue = 60.0f * (6.0f + bnorm - gnorm);
                }
                if(green == maxval) {
                    hue = 60.0f * (2.0f + rnorm - bnorm);
                }
                if(blue == maxval) {
                    hue = 60.0f * (4.0f + gnorm - rnorm);
                }
                if(hue > 360.0f) {
                    hue = hue - 360.0f;
                }
            }
        }

        private void ToRGB() {
            if(saturation == 0.0) { // Grauton, einfacher Fall
                red = (byte)(luminance * 255.0F);
                green = red;
                blue = red;
            } else {
                float rm1;
                float rm2;
                if(luminance <= 0.5f) {
                    rm2 = luminance + luminance * saturation;
                } else {
                    rm2 = luminance + saturation - luminance * saturation;
                }
                rm1 = 2.0f * luminance - rm2;
                red = ToRGB1(rm1, rm2, hue + 120.0f);
                green = ToRGB1(rm1, rm2, hue);
                blue = ToRGB1(rm1, rm2, hue - 120.0f);
            }
        }

        private byte ToRGB1(float rm1, float rm2, float rh) {
            if(rh > 360.0f) {
                rh -= 360.0f;
            } else if(rh < 0.0f) {
                rh += 360.0f;
            }

            if(rh < 60.0f) {
                rm1 = rm1 + (rm2 - rm1) * rh / 60.0f;
            } else if(rh < 180.0f) {
                rm1 = rm2;
            } else if(rh < 240.0f) {
                rm1 = rm1 + (rm2 - rm1) * (240.0f - rh) / 60.0f;
            }
            return (byte)(rm1 * 255);
        }
    }
}